#!/usr/bin/ruby -Ku
# -*- coding: utf-8 -*-
require 'test/unit'
require 'm3g'

# -----------------------------------------------------------------
# カメラを指定してスクリーンをピックする方のpick関数のテスト
# このテストは本当に簡単なテストしてかしていないので
# m3g-viewerあたりを使って別途検証が必要。
# 実際問題これが通ればほぼ問題ないと思われるが。
# -----------------------------------------------------------------

class Test_picking_1 < Test::Unit::TestCase
  def setup
  end

  def teadown
  end

  # ----------------------------------------------------------------------------------------
  # スクリーン座標を指定してピックするテスト
  # 座標はすべてsample/test-01-simpleと同じ
  # Cameraは(0,0,5)でnear=0.1,far=1000
  # Meshは(0,0,0)を中心に左上(-1,1)、右下(1,-1)
  # 内部的にピックはMeshのローカル座標で行われる。
  # レイ光線の方向ベクトルは正規化されて帰ってくるが、M3Gの要件では定義されていない独自仕様。
  # ----------------------------------------------------------------------------------------
  def test_pick_mesh_from_screen
    positions             = M3G::VertexArray.new 4, 3, 2
    positions.values[0,4] = [1,-1,0, 1,1,0, -1,-1,0, -1,1,0]

    normals             = M3G::VertexArray.new 4, 3, 1
    normals.values[0,4] = [0,0,127, 0,0,127, 0,0,127, 0,0,127]

    vertices = M3G::VertexBuffer.new
    vertices.positions = positions, 1, [0,0,0]
    vertices.normals   = normals

    tris = M3G::TriangleStripArray.new [0,1,2,3], [4]
    app  = M3G::Appearance.new
    mesh = M3G::Mesh.new vertices, tris, app

    cam = M3G::Camera.new
    cam.perspective = 45, 1, 0.1, 100
    cam.look_at 0,0,5, 0,0,0, 0,1,0

    wld = M3G::World.new
    wld.child << mesh
    wld.child << cam
    wld.active_camera = cam

    ri = M3G::RayIntersection.new

    # ど真ん中のピック.
    hit, ri = wld.pick -1, 0.5, 0.5, cam

    assert_equal true, hit
    assert_equal mesh, ri.intersected

    # レイ光線のdistanceは、
    # 「near面から」メッシュまでの距離 = 4.9  (5.0ではない)
    assert_in_delta 4.9, ri.distance, 0.0001

    ray = ri.ray

    # レイはorg=(0,0,4.9),dir=(0,0,-1)
    # 起点はnear面から
    assert_in_delta 0.0,   ray[0], 0.0001  # ox
    assert_in_delta 0.0,   ray[1], 0.0001  # oy
    assert_in_delta 4.9,   ray[2], 0.0001  # oz
    assert_in_delta 0.0,   ray[3], 0.0001  # dx
    assert_in_delta 0.0,   ray[4], 0.0001  # dy
    assert_in_delta -1.0,  ray[5], 0.0001  # dz

    # 法線は(0,0,1)
    # (法線はchar型で保存しているので誤差が大きい)
    assert_in_delta 0.0,  ri.normal.x, 0.01  # nx
    assert_in_delta 0.0,  ri.normal.y, 0.01  # ny
    assert_in_delta 1.0,  ri.normal.z, 0.01  # nz

    # サブメッシュは0
    assert_equal 0, ri.submesh_index

    # テクスチャーはundefined.
    # (の時は0を返す実装)
    assert_in_delta 0.0, ri.texture[0].s, 0.0001  # s
    assert_in_delta 0.0, ri.texture[0].t, 0.0001  # t
    
    # 端っこのピック
    # これはヒットしない
    hit, ri = wld.pick -1, 1, 1, cam

    assert_equal false, hit

  end
    
    # ---------------------------------------------------------------------------------------
    # 座標は上と若干異なる。sample/test-12-spriteに準拠
    # Cameraは(0,0,5)でnear=1,far=100
    # Sprite3Dは(0,0,0)を中心にscaled.
    # 内部的にピックはNDC座標で行われる。
    # レイ光線の方向ベクトルは正規化されて帰ってくるが、M3Gの要件では定義されていない独自仕様。
    # ----------------------------------------------------------------------------------------
  def test_pick_sprite_from_screen
    img = M3G::Image2D.new M3G::Image2D::RGB, 128, 128
    app = M3G::Appearance.new
    spr = M3G::Sprite3D.new true, img, app
    #spr->translate (1,0,0);
      
    cam = M3G::Camera.new
    cam.perspective = 45, 1, 1, 100
    cam.look_at 0,0,5, 0,0,0, 0,1,0
    
    wld = M3G::World.new
    wld.child << spr
    wld.child << cam
    wld.active_camera = cam
    
    ri = M3G::RayIntersection.new
    
    hit, ri = wld.pick -1, 0.5, 0.5, cam
    
    assert_equal true, hit
    
    assert_equal spr, ri.intersected
    
    # near面からメッシュまでの距離 = 4.0
      assert_in_delta 4.0, ri.distance, 0.0001
    
    ray = ri.ray
    
    # レイはorg=(0,0,4),dir=(0,0,-1)
    # 起点はnear面から
    assert_in_delta 0.0,   ray[0], 0.0001  # ox
    assert_in_delta 0.0,   ray[1], 0.0001  # oy
    assert_in_delta 4.0,   ray[2], 0.0001  # oz
    assert_in_delta 0.0,   ray[3], 0.0001  # dx
    assert_in_delta 0.0,   ray[4], 0.0001  # dy
    assert_in_delta -1.0,  ray[5], 0.0001  # dz
    
    # 法線は必ず(0,0,1)
    assert_in_delta 0.0,  ri.normal.x, 0.0001  # nx
    assert_in_delta 0.0,  ri.normal.y, 0.0001  # ny
    assert_in_delta 1.0,  ri.normal.z, 0.0001  # nz

    # サブメッシュはundefined.
    # (の時は0を返す実装になっている)
    assert_equal 0, ri.submesh_index
    
    # (s,t)=(0.5,0.5)を期待しているが、なぜか(0.5,0.5745)が返る。
    # 精度が悪いのが気になるが現在はこれで保留。
    assert_in_delta 0.5, ri.texture[0].s, 0.1  # s
    assert_in_delta 0.5, ri.texture[0].t, 0.1  # t
  end
end
